<template><div><h2 id="活动记录-active-record" tabindex="-1"><a class="header-anchor" href="#活动记录-active-record"><span>活动记录（Active Record）</span></a></h2>
<p><a href="http://zh.wikipedia.org/wiki/Active_Record" target="_blank" rel="noopener noreferrer">Active Record</a> 提供了一个面向对象的接口，用以访问和操作数据库中的数据，是一种兼具 <a href="https://zh.wikipedia.org/wiki/ORM" title="ORM（对象关系映射）" target="_blank" rel="noopener noreferrer">ORM（对象关系映射）</a> 和对象的关联和聚集的框架，以下简称 AR。AR 类与数据库表关联，AR 实例对应于该表的一行，AR 实例的 <em>属性</em> 表示该行中特定列的值。您可以访问 AR 属性并调用 AR 方法来访问和操作存储在数据库表中的数据，而不用编写原始 SQL 语句。</p>
<p>例如，假定 <code v-pre>Customer</code> AR 类关联着 <code v-pre>customer</code> 表，且该类的 <code v-pre>name</code> 属性代表 <code v-pre>customer</code> 表的 <code v-pre>name</code> 列。你可以写以下代码在 <code v-pre>customer</code> 表里插入一行新的记录：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Customer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">name</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'Qiang'</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>对于 MySQL，以上代码和使用下面的原生 SQL 语句是等效的，但显然前者更直观，更不易出错，并且面对不同的 DBMS（数据库系统 Database Management System）时，更不容易产生兼容性问题。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// INSERT INTO `customer` (`name`) VALUES ('Qiang')</span></span>
<span class="line"><span class="token variable">$db</span><span class="token operator">-></span><span class="token function">createCommand</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'INSERT INTO `customer` (`name`) VALUES (:name)'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">':name'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'Qiang'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="支持的数据库" tabindex="-1"><a class="header-anchor" href="#支持的数据库"><span>支持的数据库</span></a></h2>
<p>Yii 为以下“关系型数据库”提供 AR 支持：</p>
<ul>
<li><code v-pre>MySQL</code>：要求 &gt;= 4.1，通过 <code v-pre>yii\db\ActiveRecord</code> 支持</li>
<li><code v-pre>MariaDB</code>：要求 &gt;= 4.1，通过 <code v-pre>yii\db\ActiveRecord</code> 支持</li>
<li><code v-pre>PostgreSQL</code>：要求 &gt;= 7.3，通过 <code v-pre>yii\db\ActiveRecord</code> 支持</li>
<li><code v-pre>SQLite</code>：要求 &gt;=2，通过 <code v-pre>yii\db\ActiveRecord</code> 支持</li>
<li><code v-pre>Microsoft SQL Server</code>：要求 &gt;= 2008，通过 <code v-pre>yii\db\ActiveRecord</code> 支持</li>
<li><code v-pre>Oracle</code>：通过 <code v-pre>yii\db\ActiveRecord</code> 支持</li>
<li><code v-pre>CUBRID</code>：要求 &gt;= 9.3，通过 <code v-pre>yii\db\ActiveRecord</code> 支持（由于 CUBRID PDO 扩展的 <a href="http://jira.cubrid.org/browse/APIS-658" target="_blank" rel="noopener noreferrer">bug</a>，给变量加引用将不起作用，所以你得使用 CUBRID 9.3 客户端及服务端）。</li>
</ul>
<p>而且，Yii 的 AR 也支持一些全文搜索引擎：</p>
<ul>
<li><code v-pre>Sphinx</code>：要求 &gt;= 2.0，通过 <code v-pre>yii\sphinx\ActiveRecord</code> 支持，依赖官方扩展 <a href="https://github.com/yiisoft/yii2-sphinx" title="yii2-sphinx" target="_blank" rel="noopener noreferrer">yii2-sphinx</a></li>
<li><code v-pre>ElasticSearch</code>：要求 1.6.0 - 1.7.6 或 &gt;= 5.x，通过 <code v-pre>yii\elasticsearch\ActiveRecord</code> 支持，依赖官方扩展 <a href="https://github.com/yiisoft/yii2-elasticsearch" title="yii2-elasticsearch" target="_blank" rel="noopener noreferrer">yii2-elasticsearch</a></li>
</ul>
<p>此外，Yii 的 AR 功能还支持以下 NoSQL 数据库：</p>
<ul>
<li><code v-pre>Redis</code>：要求 &gt;= 2.6.12，通过 <code v-pre>yii\redis\ActiveRecord</code> 支持，依赖官方扩展 <a href="https://github.com/yiisoft/yii2-redis" title="yii2-redis" target="_blank" rel="noopener noreferrer">yii2-redis</a></li>
<li><code v-pre>MongoDB</code>：要求 &gt;= 1.3.0，通过 <code v-pre>yii\mongodb\ActiveRecord</code> 支持，依赖官方扩展 <a href="https://github.com/yiisoft/yii2-mongodb" title="yii2-mongodb" target="_blank" rel="noopener noreferrer">yii2-mongodb</a></li>
</ul>
<p>在本文中，我们会主要描述对关系型数据库的 AR 用法。然而，绝大多数的内容在 NoSQL 的 AR 里同样适用。</p>
<h2 id="声明-ar-类" tabindex="-1"><a class="header-anchor" href="#声明-ar-类"><span>声明 AR 类</span></a></h2>
<p>要想声明一个 AR 类，你需要声明该类并继承 <code v-pre>yii\db\ActiveRecord</code> 或其子类。</p>
<h3 id="设置表名" tabindex="-1"><a class="header-anchor" href="#设置表名"><span>设置表名</span></a></h3>
<p>默认的，每个 AR 类关联各自的数据库表。经过 [[yii\helpers\Inflector::camel2id()]] 处理，[[yii\db\ActiveRecord::tableName()]] 方法默认返回的表名称是通过类名转换来得。如果这个默认名称不正确，你得重写这个方法。</p>
<p>此外，[[yii\db\Connection::$tablePrefix]] 表前缀也会起作用。例如，如果 [[yii\db\Connection::$tablePrefix]] 表前缀是 <code v-pre>tbl_</code>，<code v-pre>Customer</code> 的类名将转换成 <code v-pre>tbl_customer</code> 表名，<code v-pre>OrderItem</code> 转换成 <code v-pre>tbl_order_item</code>。</p>
<p>如果你定义的表名是 <code v-pre>{{%TableName}}</code>，百分比字符 <code v-pre>%</code> 会被替换成表前缀。例如，<code v-pre>{{%post}}</code> 会变成 <code v-pre>{{tbl_post}}</code>。表名两边的括号会被 <a href="https://learnku.com/docs/yii-framework/2.0.x/dao/12023#3f37f2" target="_blank" rel="noopener noreferrer">SQL 查询引用</a> 处理。</p>
<p>下面的例子中，我们给 <code v-pre>customer</code> 数据库表定义叫 <code v-pre>Customer</code> 的 AR 类。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>models</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">const</span> <span class="token constant">STATUS_INACTIVE</span> <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">const</span> <span class="token constant">STATUS_ACTIVE</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * <span class="token keyword">@return</span> <span class="token class-name"><span class="token keyword">string</span></span> Active Record 类关联的数据库表名称</span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">tableName</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token string single-quoted-string">'{{customer}}'</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="将-ar-称为模型" tabindex="-1"><a class="header-anchor" href="#将-ar-称为模型"><span>将 AR 称为模型</span></a></h3>
<p>AR 实例称为 <a href="https://learnku.com/docs/yii-framework/2.0.x/models/12003" target="_blank" rel="noopener noreferrer">模型</a>。因此，我们通常将 AR 类 放在 <code v-pre>app\models</code> 命名空间下（或者其他保存模型的命名空间）。</p>
<p>因为 <code v-pre>yii\db\ActiveRecord</code> 继承了模型 <code v-pre>yii\base\Model</code>，它就拥有所有模型特性，比如说属性（attributes），验证规则（rules），数据序列化（data serialization）等。</p>
<h2 id="连接数据库" tabindex="-1"><a class="header-anchor" href="#连接数据库"><span>连接数据库</span></a></h2>
<p>活动记录 AR 类，默认使用 <code v-pre>db</code> <a href="https://learnku.com/docs/yii-framework/2.0.x/component/12001#241bbb" target="_blank" rel="noopener noreferrer">组件</a> 作为连接器 <code v-pre>yii\db\Connection</code> 访问和操作数据库数据。基于 <a href="https://learnku.com/docs/yii-framework/2.0.x/dao/12023" target="_blank" rel="noopener noreferrer">数据库访问</a> 中的解释，你可以在系统配置中这样配置 <code v-pre>db</code> 组件。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'components'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'db'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'yii\db\Connection'</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'dsn'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'mysql:host=localhost;dbname=testdb'</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'username'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'demo'</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'password'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'demo'</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>如果你要用不同的数据库连接，而不仅仅是 <code v-pre>db</code> 组件，你可以重写 [[yii\db\ActiveRecord::getDb()]] 方法。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">getDb</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// 使用 "db2" 组件</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">db2</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="查询数据" tabindex="-1"><a class="header-anchor" href="#查询数据"><span>查询数据</span></a></h2>
<p>定义 AR 类后，你可以从相应的数据库表中查询数据。查询过程大致如下三个步骤：</p>
<ol>
<li>通过 [[yii\db\ActiveRecord::find()]] 方法创建一个新的查询生成器对象；</li>
<li>使用 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-query-builder.md#building-queries" target="_blank" rel="noopener noreferrer">查询生成器的构建方法</a> 来构建你的查询；</li>
<li>调用 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-query-builder.md#query-methods" target="_blank" rel="noopener noreferrer">查询生成器的查询方法</a> 来取出数据到 AR 实例中。</li>
</ol>
<h3 id="活动查询-active-query" tabindex="-1"><a class="header-anchor" href="#活动查询-active-query"><span>活动查询（Active Query）</span></a></h3>
<p>正如上面你看到的，是不是跟 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-query-builder.md" target="_blank" rel="noopener noreferrer">查询生成器</a> 的步骤差不多。唯一有区别的地方在于，你用 [[yii\db\ActiveRecord::find()]] 去获得一个新的查询生成器对象，这个对象是 <code v-pre>yii\db\ActiveQuery</code>，而不是使用 <code v-pre>new</code> 操作符创建一个查询生成器对象。</p>
<p>下面是一些例子，介绍如何使用 Active Query 查询数据：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 返回 ID 为 123 的客户：</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `id` = 123</span></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">where</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token number">123</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">one</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 取回所有活跃客户并以他们的 ID 排序：</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `status` = 1 ORDER BY `id`</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">where</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token constant">STATUS_ACTIVE</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">orderBy</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 取回活跃客户的数量：</span></span>
<span class="line"><span class="token comment">// SELECT COUNT(*) FROM `customer` WHERE `status` = 1</span></span>
<span class="line"><span class="token variable">$count</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">where</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token constant">STATUS_ACTIVE</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">count</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 以客户 ID 索引结果集：</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `customer`</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">indexBy</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码中，<code v-pre>$customer</code> 是个 <code v-pre>Customer</code> 对象，而 <code v-pre>$customers</code> 是个以 <code v-pre>Customer</code> 对象为元素的数组。它们两都是以 <code v-pre>customer</code> 表中取回的数据结果集填充的。</p>
<blockquote>
<p>Tip: 由于 <code v-pre>yii\db\ActiveQuery</code> 继承 <code v-pre>yii\db\Query</code>，你可以使用 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-query-builder.md" target="_blank" rel="noopener noreferrer">查询构造器</a> 章节里所描述的 <em>所有</em> 查询方法。</p>
</blockquote>
<h3 id="活动查询快捷方法" tabindex="-1"><a class="header-anchor" href="#活动查询快捷方法"><span>活动查询快捷方法</span></a></h3>
<p>根据主键获取数据行是比较常见的操作，所以 Yii 提供了两个快捷方法：</p>
<ul>
<li><code v-pre>yii\db\ActiveRecord::findOne()</code>：返回一个 AR 实例，填充于查询结果的第一行数据。</li>
<li><code v-pre>yii\db\ActiveRecord::findAll()</code>：返回一个 AR 实例的数据，填充于查询结果的全部数据。</li>
</ul>
<p>这两个方法的传参格式如下：</p>
<ul>
<li>标量值：这个值会当作主键去查询。Yii 会通过读取数据库模式信息来识别主键列。</li>
<li>标量值的数组：这数组里的值都当作要查询的主键的值。</li>
<li>关联数组：键值是表的列名，元素值是相应的要查询的条件值。可以到 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-query-builder.md#hash-format" target="_blank" rel="noopener noreferrer">哈希格式</a> 查看更多信息。</li>
</ul>
<p>如下代码描述如何使用这些方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 返回 id 为 123 的客户</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `id` = 123</span></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 返回 id 是 100, 101, 123, 124 的客户</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `id` IN (100, 101, 123, 124)</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findAll</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">101</span><span class="token punctuation">,</span> <span class="token number">123</span><span class="token punctuation">,</span> <span class="token number">124</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 返回 id 是 123 的活跃客户</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `id` = 123 AND `status` = 1</span></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token number">123</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token constant">STATUS_ACTIVE</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 返回所有不活跃的客户</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `status` = 0</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findAll</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token constant">STATUS_INACTIVE</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Warning: 如果你需要将用户输入传递给这些方法，请确保输入值是标量或者是数组条件，确保数组结构不能被外部所改变：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// yii\web\Controller 确保了 $id 是标量</span></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionView</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$model</span> <span class="token operator">=</span> <span class="token class-name static-context">Post</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></blockquote>
<p>// 明确了指定要搜索的列，在此处传递标量或数组将始终只是查找出单个记录而已<br>
$model = Post::findOne([‘id’ =&gt; Yii::$app-&gt;request-&gt;get(‘id’)]);</p>
<p>// 不要使用下面的代码！可以注入一个数组条件来匹配任意列的值！<br>
$model = Post::findOne(Yii::$app-&gt;request-&gt;get(‘id’));</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"></span>
<span class="line"><span class="token operator">></span> Tip<span class="token punctuation">:</span> <span class="token string backtick-quoted-string">`yii\db\ActiveRecord::findOne()`</span> 和 <span class="token string backtick-quoted-string">`yii\db\ActiveQuery::one()`</span> 都不会添加 <span class="token string backtick-quoted-string">`LIMIT 1`</span> 到生成的 <span class="token constant">SQL</span> 语句中。如果你的查询会返回很多行的数据，你明确的应该加上 <span class="token string backtick-quoted-string">`limit(1)`</span> 来提高性能，比如 <span class="token string backtick-quoted-string">`Customer::find()->limit(1)->one()`</span>。</span>
<span class="line"></span>
<span class="line">除了使用查询生成器的方法之外，你还可以书写原生的 <span class="token constant">SQL</span> 语句来查询数据，并填充结果集到 <span class="token constant">AR</span> 对象中。通过使用 <span class="token string backtick-quoted-string">`yii\db\ActiveRecord::findBySql()`</span> 方法：</span>
<span class="line"></span>
<span class="line"><span class="token string backtick-quoted-string">``</span>`php</span>
<span class="line"><span class="token comment">// 返回所有不活跃的客户</span></span>
<span class="line"><span class="token variable">$sql</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'SELECT * FROM customer WHERE status=:status'</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findBySql</span><span class="token punctuation">(</span><span class="token variable">$sql</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">':status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token constant">STATUS_INACTIVE</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>不要在 <code v-pre>yii\db\ActiveRecord::findBySql()</code> 方法后加其他查询方法，多余的查询方法都会被忽略。</p>
<h2 id="访问数据" tabindex="-1"><a class="header-anchor" href="#访问数据"><span>访问数据</span></a></h2>
<p>如上所述，从数据库返回的数据被填充到 AR 实例中，查询结果的每一行对应于单个 AR 实例。您可以通过 AR 实例的属性来访问列值，例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// "id" 和 "email" 是 "customer" 表中的列名</span></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$id</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">id</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$email</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">email</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Tip: AR 的属性以区分大小写的方式为相关联的表列命名的。Yii 会自动为关联表的每一列定义 AR 中的一个属性。您不应该重新声明任何属性。</p>
</blockquote>
<p>由于 AR 的属性以表的列名命名，可能你会发现你正在编写像这样的 PHP 代码：<code v-pre>$customer-&gt;first_name</code>，如果你的表的列名是使用下划线分隔的，那么属性名中的单词以这种方式命名。如果您担心代码风格一致性的问题，那么你应当重命名相应的表列名（例如使用骆驼拼写法）。</p>
<h3 id="数据转换" tabindex="-1"><a class="header-anchor" href="#数据转换"><span>数据转换</span></a></h3>
<p>常常遇到，要输入或显示的数据是一种格式，而要将其存储在数据库中是另一种格式。例如，在数据库中，您将客户的生日存储为 UNIX 时间戳（虽然这不是一个很好的设计），而在大多数情况下，你想以字符串 <code v-pre>'YYYY/MM/DD'</code> 的格式处理生日数据。为了实现这一目标，您可以在 <code v-pre>Customer</code> 中定义 <em>数据转换</em> 方法 定义 AR 类如下：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getBirthdayText</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token function">date</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Y/m/d'</span><span class="token punctuation">,</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">birthday</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">setBirthdayText</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">birthday</span> <span class="token operator">=</span> <span class="token function">strtotime</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>现在你的 PHP 代码中，你可以访问 <code v-pre>$customer-&gt;birthdayText</code>，来以 <code v-pre>'YYYY/MM/DD'</code> 的格式输入和显示客户生日，而不是访问 <code v-pre>$customer-&gt;birthday</code>。</p>
<blockquote>
<p>Tip: 上述示例显示了以不同格式转换数据的通用方法。如果你正在使用日期值，您可以使用 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/tutorial-core-validators.md#date" target="_blank" rel="noopener noreferrer">DateValidator</a> 和 [[yii\jui\DatePicker]] 日期选择器来操作， 这将更易用，更强大。</p>
</blockquote>
<h3 id="以数组形式获取数据" tabindex="-1"><a class="header-anchor" href="#以数组形式获取数据"><span>以数组形式获取数据</span></a></h3>
<p>通过 AR 对象获取数据十分方便灵活，与此同时，当你需要返回大量的数据的时候，这样的做法并不令人满意，因为这将导致大量内存占用。在这种情况下，您可以在查询方法前调用 <code v-pre>yii\db\ActiveQuery::asArray()</code> 方法，来获取 PHP 数组形式的结果：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 返回所有客户，每个客户返回一个关联数组</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">asArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Tip: 虽然这种方法可以节省内存并提高性能，但它更靠近较低的 DB 抽象层，你将失去大部分的 AR 提供的功能。一个非常重要的区别在于列值的数据类型。当您在 AR 实例中返回数据时，列值将根据实际列类型，自动类型转换；然而，当您以数组返回数据时，列值将为字符串（因为它们是没有处理过的 PDO 的结果），不管它们的实际列是什么类型。</p>
</blockquote>
<h3 id="批量获取数据" tabindex="-1"><a class="header-anchor" href="#批量获取数据"><span>批量获取数据</span></a></h3>
<p>在 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-query-builder.md" target="_blank" rel="noopener noreferrer">查询生成器</a> 中，我们已经解释过，每当从数据库查询大量数据时，可以使用 <em>批处理查询</em> 来最小化你的内存使用。你可以在 AR 中使用同样的技巧。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 每次获取 10 条客户数据</span></span>
<span class="line"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">batch</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token variable">$customers</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// $customers 是个最多拥有 10 条数据的数组</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 每次获取 10 条客户数据，然后一条一条迭代它们</span></span>
<span class="line"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">each</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token variable">$customer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// $customer 是个 `Customer` 对象</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 贪婪加载模式的批处理查询</span></span>
<span class="line"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">each</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token variable">$customer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// $customer 是个 `Customer` 对象，并附带关联的 `'orders'`</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="保存数据" tabindex="-1"><a class="header-anchor" href="#保存数据"><span>保存数据</span></a></h2>
<p>使用 AR，您可以通过以下步骤，轻松地将数据保存到数据库：</p>
<ol>
<li>准备一个 AR 实例</li>
<li>将新值赋给 AR 的属性</li>
<li>调用 [[yii\db\ActiveRecord::save()]] 保存数据到数据库中。</li>
</ol>
<p>例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 插入新记录</span></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Customer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">name</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'James'</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">email</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'james@example.com'</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 更新已存在的记录</span></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">email</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'james@newexample.com'</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>[[yii\db\ActiveRecord::save()]] 方法可能插入或者更新表的记录，这取决于 AR 实例的状态。 如果实例通过 <code v-pre>new</code> 操作符实例化，调用 <code v-pre>save()</code> 方法，将插入新记录；如果实例是一个查询方法的结果，调用 <code v-pre>save()</code> 方法，将更新这个实例对应的表记录行。</p>
<p>你可以通过检查 AR 实例的 [[yii\db\ActiveRecord::isNewRecord]] 属性值来区分这两个状态。此属性也被使用在 [[yii\db\ActiveRecord::save()]] 方法内部，代码如下：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">save</span><span class="token punctuation">(</span><span class="token variable">$runValidation</span> <span class="token operator">=</span> <span class="token constant boolean">true</span><span class="token punctuation">,</span> <span class="token variable">$attributeNames</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token function">getIsNewRecord</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">insert</span><span class="token punctuation">(</span><span class="token variable">$runValidation</span><span class="token punctuation">,</span> <span class="token variable">$attributeNames</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">update</span><span class="token punctuation">(</span><span class="token variable">$runValidation</span><span class="token punctuation">,</span> <span class="token variable">$attributeNames</span><span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token constant boolean">false</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Tip: 你可以直接调用 [[yii\db\ActiveRecord::insert()]] 或者 [[yii\db\ActiveRecord::update()]] 方法来插入或更新一条记录。</p>
</blockquote>
<h3 id="数据验证" tabindex="-1"><a class="header-anchor" href="#数据验证"><span>数据验证</span></a></h3>
<p>因为 <code v-pre>yii\db\ActiveRecord</code> 继承于 <code v-pre>yii\base\Model</code>，它共享相同的 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/input-validation.md" target="_blank" rel="noopener noreferrer">输入验证</a> 功能。你可以通过重写 [[yii\db\ActiveRecord::rules()]] 方法声明验证规则并执行，通过调用 [[yii\db\ActiveRecord::validate()]] 方法进行数据验证。</p>
<p>当你调用 [[yii\db\ActiveRecord::save()]] 时，默认情况下会自动调用 [[yii\db\ActiveRecord::validate()]]。只有当验证通过时，它才会真正地保存数据；否则将简单地返回 <code v-pre>false</code>，您可以检查 [[yii\db\ActiveRecord::errors]] 属性来获取验证过程的错误消息。</p>
<blockquote>
<p>Tip: 如果你确定你的数据不需要验证（比如说数据来自可信的场景），你可以调用 <code v-pre>$model-&gt;save(false);</code> 来跳过验证过程。</p>
</blockquote>
<h3 id="块赋值" tabindex="-1"><a class="header-anchor" href="#块赋值"><span>块赋值</span></a></h3>
<p>和普通的 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-models.md" target="_blank" rel="noopener noreferrer">模型</a> 一样，你亦可以享受 AR 实例的 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-models.md#massive-assignment" target="_blank" rel="noopener noreferrer">块赋值</a> 特性。使用此功能，您可以在单个 PHP 语句中，给 AR 实例的多个属性批量赋值，如下所示。记住，只有 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-models.md#safe-attributes" target="_blank" rel="noopener noreferrer">安全属性</a> 才可以批量赋值。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$values</span> <span class="token operator">=</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'name'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'James'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'email'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'james@example.com'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Customer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">attributes</span> <span class="token operator">=</span> <span class="token variable">$values</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="更新计数器" tabindex="-1"><a class="header-anchor" href="#更新计数器"><span>更新计数器</span></a></h3>
<p>在数据库表中增加或减少一个字段的值是个常见的任务。我们将这些列称为“计数列”。您可以使用 [[yii\db\ActiveRecord::updateCounters()]] 更新一个或多个计数列。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$post</span> <span class="token operator">=</span> <span class="token class-name static-context">Post</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// UPDATE `post` SET `view_count` = `view_count` + 1 WHERE `id` = 100</span></span>
<span class="line"><span class="token variable">$post</span><span class="token operator">-></span><span class="token function">updateCounters</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'view_count'</span> <span class="token operator">=></span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Note: 如果你使用 [[yii\db\ActiveRecord::save()]] 更新一个计数列，你最终将得到错误的结果，因为可能发生这种情况，多个请求间并发读写同一个计数列。</p>
</blockquote>
<h3 id="脏属性" tabindex="-1"><a class="header-anchor" href="#脏属性"><span>脏属性</span></a></h3>
<p>当您调用 [[yii\db\ActiveRecord::save()]] 保存 AR 实例时，只有 <em>脏属性</em> 被保存。如果一个属性的值已被修改，则会被认为是 <em>脏</em>，因为它是从 DB 加载出来的或者刚刚保存到 DB。请注意，无论如何 AR 都会执行数据验证，不管有没有脏属性。</p>
<p>AR 自动维护脏属性列表。它保存所有属性的旧值，并其与最新的属性值进行比较，就是酱紫个道理。你可以调用 [[yii\db\ActiveRecord::getDirtyAttributes()]] 获取当前的脏属性。你也可以调用 [[yii\db\ActiveRecord::markAttributeDirty()]] 将属性显式标记为脏。</p>
<p>如果你有需要获取属性原先的值，你可以调用 [[yii\db\ActiveRecord::getOldAttributes()]] 或者 [[yii\db\ActiveRecord::getOldAttribute()]]。</p>
<blockquote>
<p>注：属性新旧值的比较是用 <code v-pre>===</code> 操作符，所以一样的值但类型不同，依然被认为是脏的。当模型从 HTML 表单接收用户输入时，通常会出现这种情况，其中每个值都表示为一个字符串类型。为了确保正确的类型，比如，整型需要用 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/input-validation.md#data-filtering" target="_blank" rel="noopener noreferrer">过滤验证器</a>： <code v-pre>['attributeName', 'filter', 'filter' =&gt; 'intval']</code>。其他 PHP 类型转换函数一样适用，像 <a href="https://www.php.net/manual/zh/function.intval.php" target="_blank" rel="noopener noreferrer">intval()</a>， <a href="https://www.php.net/manual/zh/function.floatval.php" target="_blank" rel="noopener noreferrer">floatval()</a>， <a href="https://www.php.net/manual/zh/function.boolval.php" target="_blank" rel="noopener noreferrer">boolval</a>等。</p>
</blockquote>
<h3 id="默认属性值" tabindex="-1"><a class="header-anchor" href="#默认属性值"><span>默认属性值</span></a></h3>
<p>某些表列可能在数据库中定义了默认值。有时，你可能想预先填充具有这些默认值的 AR 实例的 Web 表单。为了避免再次写入相同的默认值，您可以调用 <code v-pre>yii\db\ActiveRecord::loadDefaultValues()</code> 来填充 DB 定义的默认值进入相应的 AR 属性：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Customer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">loadDefaultValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// $customer->xyz 将被 “zyz” 列定义的默认值赋值</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="属性类型转换" tabindex="-1"><a class="header-anchor" href="#属性类型转换"><span>属性类型转换</span></a></h3>
<p>在查询结果填充 <code v-pre>yii\db\ActiveRecord</code> 时，将自动对其属性值执行类型转换，基于 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-dao.md#database-schema" target="_blank" rel="noopener noreferrer">数据库表模式</a> 中的信息。这允许从数据表中获取数据，声明为整型的，使用 PHP 整型填充 AR 实例，布尔值（boolean）的也用布尔值填充等。但是，类型转换机制有几个限制：</p>
<ul>
<li>浮点值不被转换，并且将被表示为字符串，否则它们可能会使精度降低。</li>
<li>整型值的转换取决于您使用的操作系统的整数容量。尤其是：声明为“无符号整型”或“大整型”的列的值将仅转换为 64 位操作系统的 PHP 整型，而在 32 位操作系统中，它们将被表示为字符串。</li>
</ul>
<p>值得注意的是，只有在从查询结果填充 AR 实例时才执行属性类型转换。而从 HTTP 请求加载的值或直接通过属性访问赋值的，没有自动转换。在准备用于在 AR 保存时，准备 SQL 语句还使用了表模式，以确保查询时值绑定到具有正确类型的。但是，AR 实例的属性值不会在保存过程中转换。</p>
<blockquote>
<p>Tip: 你可以在 AR 验证或者保存过程中，使用 <code v-pre>yii\behaviors\AttributeTypecastBehavior</code> 来简化属性的类型转换。</p>
</blockquote>
<h4 id="json-mysql-和-postgresql" tabindex="-1"><a class="header-anchor" href="#json-mysql-和-postgresql"><span>JSON（MySQL 和 PostgreSQL）</span></a></h4>
<p>从 2.0.14 开始，Yii AR 支持了更多的复杂数据类型，例如 JSON 或多维数组。数据填充后，基于 JSON 标准解码规则，来自 JSON 列的值将自动解码。</p>
<p>另一方面，为了将属性值保存到 JSON 列中，AR 会自动创建一个 <code v-pre>yii\db\JsonExpression</code> 对象，这对象将在 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-query-builder.md" target="_blank" rel="noopener noreferrer">QueryBuilder</a> 层被编码成 JSON 字符串。</p>
<h4 id="数组-postgresql" tabindex="-1"><a class="header-anchor" href="#数组-postgresql"><span>数组（PostgreSQL）</span></a></h4>
<p>数据填充后，来自数组列的值将自动从 PgSQL 的编码值解码为一个 <code v-pre>yii\db\ArrayExpression</code> 对象。它继承于 PHP 的 <code v-pre>ArrayAccess</code> 接口，所以你可以把它当作一个数组用，或者调用 <code v-pre>-&gt;getValue()</code> 来获取数组本身。</p>
<p>另一方面，为了将属性值保存到数组列，AR 会自动创建一个 <code v-pre>yii\db\ArrayExpression</code> 对象， 这对象将在 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-query-builder.md" target="_blank" rel="noopener noreferrer">QueryBuilder</a> 中被编码成数组的 PgSQL 字符串表达式。</p>
<p>你还可以这样使用数组列的条件：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$query</span><span class="token operator">-></span><span class="token function">andWhere</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'='</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'json'</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">ArrayExpression</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'foo'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'bar'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>要详细了解表达式构建系统，可以访问 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-query-builder.md#adding-custom-conditions-and-expressions" target="_blank" rel="noopener noreferrer">Query Builder – 增加自定义条件和语句</a> 文章。</p>
<h3 id="更新多数据行" tabindex="-1"><a class="header-anchor" href="#更新多数据行"><span>更新多数据行</span></a></h3>
<p>上述方法都可以用于单个 AR 实例，以插入或更新单条表数据行。要同时更新多个数据行，你应该调用 <code v-pre>yii\db\ActiveRecord::updateAll()</code> 这是一个静态方法。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com%`</span></span>
<span class="line"><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">updateAll</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token constant">STATUS_ACTIVE</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'like'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'email'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'@example.com'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><p>同样，你可以调用 <code v-pre>yii\db\ActiveRecord::updateAllCounters()</code> 同时更新多条记录的计数列。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// UPDATE `customer` SET `age` = `age` + 1</span></span>
<span class="line"><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">updateAllCounters</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'age'</span> <span class="token operator">=></span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="删除数据" tabindex="-1"><a class="header-anchor" href="#删除数据"><span>删除数据</span></a></h2>
<p>要删除单行数据，首先获取与该行对应的 AR 实例，然后调用 <code v-pre>yii\db\ActiveRecord::delete()</code> 方法。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><p>你可以调用 <code v-pre>yii\db\ActiveRecord::deleteAll()</code> 方法删除多行甚至全部的数据。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">deleteAll</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token constant">STATUS_INACTIVE</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><blockquote>
<p>Tip: 调用 <code v-pre>yii\db\ActiveRecord::deleteAll()</code> 时要非常小心，因为如果在指定条件时出错，它可能会完全擦除表中的所有数据。</p>
</blockquote>
<h2 id="ar-生命周期" tabindex="-1"><a class="header-anchor" href="#ar-生命周期"><span>AR 生命周期</span></a></h2>
<p>当你实现各种功能的时候，会发现了解 AR 的生命周期很重要。在每个生命周期中，一系列的方法将被调用执行，您可以重写这些方法，以定制你要的生命周期。您还可以响应触发某些 AR 事件，以便在生命周期中注入您的自定义代码。这些事件在开发 AR 的 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-behaviors.md" target="_blank" rel="noopener noreferrer">行为</a> 时特别有用，通过行为可以定制 AR 生命周期的。</p>
<p>下面，我们将总结各种 AR 的生命周期，以及生命周期中所涉及的各种方法、事件。</p>
<h3 id="new-实例化生命周期" tabindex="-1"><a class="header-anchor" href="#new-实例化生命周期"><span>New 实例化生命周期</span></a></h3>
<p>当通过 <code v-pre>new</code> 操作符新建一个 AR 实例时，会发生以下生命周期：</p>
<ol>
<li>类的构造函数调用.</li>
<li><code v-pre>yii\db\ActiveRecord::init()</code>：触发 <code v-pre>yii\db\ActiveRecord::EVENT_INIT</code> 事件。</li>
</ol>
<h3 id="查询数据生命周期" tabindex="-1"><a class="header-anchor" href="#查询数据生命周期"><span>查询数据生命周期</span></a></h3>
<p>当通过 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md#querying-data" target="_blank" rel="noopener noreferrer">查询方法</a> 查询数据时，每个新填充出来的 AR 实例，将发生下面的生命周期：</p>
<ol>
<li>类的构造函数调用。</li>
<li><code v-pre>yii\db\ActiveRecord::init()</code>：触发 <code v-pre>yii\db\ActiveRecord::EVENT_INIT</code> 事件。</li>
<li><code v-pre>yii\db\ActiveRecord::afterFind()</code>：触发 <code v-pre>yii\db\ActiveRecord::EVENT_AFTER_FIND</code> 事件。</li>
</ol>
<h3 id="保存数据生命周期" tabindex="-1"><a class="header-anchor" href="#保存数据生命周期"><span>保存数据生命周期</span></a></h3>
<p>当通过 [[yii\db\ActiveRecord::save()]] 插入或更新 AR 实例时 会发生以下生命周期：</p>
<ol>
<li><code v-pre>yii\db\ActiveRecord::beforeValidate()</code>：触发 <code v-pre>yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE</code> 事件。如果这方法返回 <code v-pre>false</code> 或者 <code v-pre>yii\base\ModelEvent::isValid</code> 值为 <code v-pre>false</code>，接下来的步骤都会被跳过。</li>
<li>执行数据验证。如果数据验证失败，步骤 3 之后的步骤将被跳过。</li>
<li><code v-pre>yii\db\ActiveRecord::afterValidate()</code>：触发 <code v-pre>yii\db\ActiveRecord::EVENT_AFTER_VALIDATE</code> 事件。</li>
<li><code v-pre>yii\db\ActiveRecord::beforeSave()</code>：触发 <code v-pre>yii\db\ActiveRecord::EVENT_BEFORE_INSERT</code> 或者 <code v-pre>yii\db\ActiveRecord::EVENT_BEFORE_UPDATE</code> 事件。如果这方法返回 <code v-pre>false</code> 或者 <code v-pre>yii\base\ModelEvent::isValid</code> 值为 <code v-pre>false</code>，接下来的步骤都会被跳过。</li>
<li>执行真正的数据插入或者更新。</li>
<li><code v-pre>yii\db\ActiveRecord::afterSave()</code>：触发 <code v-pre>yii\db\ActiveRecord::EVENT_AFTER_INSERT</code> 或者 <code v-pre>yii\db\ActiveRecord::EVENT_AFTER_UPDATE</code> 事件。</li>
</ol>
<h3 id="删除数据生命周期" tabindex="-1"><a class="header-anchor" href="#删除数据生命周期"><span>删除数据生命周期</span></a></h3>
<p>当通过 [[yii\db\ActiveRecord::delete()]] 删除 AR 实例时，会发生以下生命周期：</p>
<ol>
<li><code v-pre>yii\db\ActiveRecord::beforeDelete()</code>：触发 <code v-pre>yii\db\ActiveRecord::EVENT_BEFORE_DELETE</code> 事件。如果这方法返回 <code v-pre>false</code> 或者 <code v-pre>yii\base\ModelEvent::isValid</code> 值为 <code v-pre>false</code>，接下来的步骤都会被跳过。</li>
<li>执行真正的数据删除。</li>
<li><code v-pre>yii\db\ActiveRecord::afterDelete()</code>：触发 <code v-pre>yii\db\ActiveRecord::EVENT_AFTER_DELETE</code> 事件。</li>
</ol>
<blockquote>
<p>Tip: 调用以下方法则不会启动上述的任何生命周期，因为这些方法直接操作数据库，而不是基于 AR 模型：</p>
<ul>
<li><code v-pre>yii\db\ActiveRecord::updateAll()</code></li>
<li><code v-pre>yii\db\ActiveRecord::deleteAll()</code></li>
<li><code v-pre>yii\db\ActiveRecord::updateCounters()</code></li>
<li><code v-pre>yii\db\ActiveRecord::updateAllCounters()</code></li>
</ul>
</blockquote>
<h3 id="刷新数据生命周期" tabindex="-1"><a class="header-anchor" href="#刷新数据生命周期"><span>刷新数据生命周期</span></a></h3>
<p>当通过 <code v-pre>yii\db\ActiveRecord::refresh()</code> 刷新 AR 实例时，如刷新成功方法返回 <code v-pre>true</code>，那么 <code v-pre>yii\db\ActiveRecord::EVENT_AFTER_REFRESH</code> 事件将被触发。</p>
<h2 id="事务操作" tabindex="-1"><a class="header-anchor" href="#事务操作"><span>事务操作</span></a></h2>
<p>AR 有两种方式来使用 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-dao.md#performing-transactions" target="_blank" rel="noopener noreferrer">事务</a>。</p>
<p>第一种方法是，在事务块中显式地包含 AR 的各个方法调用，如下所示，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">getDb</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">transaction</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token variable">$db</span><span class="token punctuation">)</span> <span class="token keyword">use</span> <span class="token punctuation">(</span><span class="token variable">$customer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">id</span> <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token comment">// ...其他 DB 操作...</span></span>
<span class="line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 或者</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$transaction</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">getDb</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">beginTransaction</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">try</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">id</span> <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token comment">// ...other DB operations...</span></span>
<span class="line">    <span class="token variable">$transaction</span><span class="token operator">-></span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span> <span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>Exception</span> <span class="token variable">$e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$transaction</span><span class="token operator">-></span><span class="token function">rollBack</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">throw</span> <span class="token variable">$e</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span> <span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>Throwable</span> <span class="token variable">$e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$transaction</span><span class="token operator">-></span><span class="token function">rollBack</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">throw</span> <span class="token variable">$e</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Tip: 在上面的代码中，我们有两个 catch 块用于兼容 PHP 5.x 和 PHP 7.x。<code v-pre>\Exception</code> 继承于 <a href="https://www.php.net/manual/zh/class.throwable.php" target="_blank" rel="noopener noreferrer"><code v-pre>\Throwable</code> interface</a> 由于 PHP 7.0 的改动，如果您的应用程序仅使用 PHP 7.0 及更高版本，您可以跳过 <code v-pre>\Exception</code> 部分。</p>
</blockquote>
<p>第二种方法是在 <code v-pre>yii\db\ActiveRecord::transactions()</code> 方法中列出需要事务支持的 DB 操作。 例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">transactions</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token string single-quoted-string">'admin'</span> <span class="token operator">=></span> <span class="token keyword static-context">self</span><span class="token operator">::</span><span class="token constant">OP_INSERT</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'api'</span> <span class="token operator">=></span> <span class="token keyword static-context">self</span><span class="token operator">::</span><span class="token class-name">OP_INSERT</span> <span class="token operator">|</span> <span class="token keyword static-context">self</span><span class="token operator">::</span><span class="token class-name">OP_UPDATE</span> <span class="token operator">|</span> <span class="token keyword static-context">self</span><span class="token operator">::</span><span class="token constant">OP_DELETE</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token comment">// 上面等价于：</span></span>
<span class="line">            <span class="token comment">// 'api' => self::OP_ALL,</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><code v-pre>yii\db\ActiveRecord::transactions()</code> 方法应当返回以 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-models.md#scenarios" target="_blank" rel="noopener noreferrer">场景</a> 为键、以需要放到事务中的 DB 操作为值的数组。以下的常量可以表示相应的 DB 操作：</p>
<ul>
<li><code v-pre>yii\db\ActiveRecord::OP_INSERT</code>：插入操作用于执行 <code v-pre>yii\db\ActiveRecord::insert()</code>；</li>
<li><code v-pre>yii\db\ActiveRecord::OP_UPDATE</code>：更新操作用于执行 <code v-pre>yii\db\ActiveRecord::update()</code>；</li>
<li><code v-pre>yii\db\ActiveRecord::OP_DELETE</code>：删除操作用于执行 <code v-pre>yii\db\ActiveRecord::delete()</code>。</li>
</ul>
<p>使用 <code v-pre>|</code> 运算符连接上述常量来表明多个操作。您也可以使用快捷常量 <code v-pre>yii\db\ActiveRecord::OP_ALL</code> 来指代上述所有的三个操作。</p>
<p>这个事务方法的原理是：相应的事务在调用 <code v-pre>yii\db\ActiveRecord::beforeSave()</code> 方法时开启，在调用 <code v-pre>yii\db\ActiveRecord::afterSave()</code> 方法时被提交。</p>
<h2 id="乐观锁" tabindex="-1"><a class="header-anchor" href="#乐观锁"><span>乐观锁</span></a></h2>
<p>乐观锁是一种，当一行数据，同时被多个用户更新的时候，防止冲突的方法。例如，同一时间内，用户 A 和用户 B 都在编辑相同的文章。用户 A 保存他的编辑后，用户 B 也点击“保存”按钮来保存他的编辑。实际上，用户 B 正在处理的是过时版本的文章，因此最好是，想办法阻止他保存文章并向他提示一些信息。</p>
<p>乐观锁，通过使用一个字段来记录每行的版本号来解决上述问题。当使用过时的版本号保存一行数据时，<code v-pre>yii\db\StaleObjectException</code> 异常将被抛出，这阻止了该行的保存。乐观锁只支持更新 <code v-pre>yii\db\ActiveRecord::update()</code> 或者删除 <code v-pre>yii\db\ActiveRecord::delete()</code> 已经存在的单条数据行。</p>
<p>使用乐观锁的步骤，如下：</p>
<ol>
<li>在与 AR 类相关联的 DB 表中创建一个列，以存储每行的版本号。这个列应当是长整型（在 MySQL 中是 <code v-pre>BIGINT DEFAULT 0</code>）。</li>
<li>重写 <code v-pre>yii\db\ActiveRecord::optimisticLock()</code> 方法返回这个列的命名。</li>
<li>在你的 Model 类里实现 <code v-pre>yii\behaviors\OptimisticLockBehavior</code> 行为（注：这个行为类在 2.0.16 版本加入），以便从请求参数里自动解析这个列的值。然后从验证规则中删除 version 属性，因为 <code v-pre>yii\behaviors\OptimisticLockBehavior</code> 已经处理它了。</li>
<li>在用于用户填写的 Web 表单中，添加一个隐藏字段来存储正在更新的行的当前版本号。</li>
<li>在使用 AR 更新数据的控制器动作中，要捕获 <code v-pre>yii\db\StaleObjectException</code> 异常。实现一些业务逻辑来解决冲突（例如合并更改，提示陈旧的数据等）。</li>
</ol>
<p>例如，假定版本列被命名为 <code v-pre>version</code>。您可以使用下面的代码来实现乐观锁。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// ------ 视图层代码 -------</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>helpers<span class="token punctuation">\</span>Html</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// ...其他输入栏</span></span>
<span class="line"><span class="token keyword">echo</span> <span class="token class-name static-context">Html</span><span class="token operator">::</span><span class="token function">activeHiddenInput</span><span class="token punctuation">(</span><span class="token variable">$model</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'version'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// ------ 控制器代码 -------</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>StaleObjectException</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionUpdate</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$model</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">findModel</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">try</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$model</span><span class="token operator">-></span><span class="token function">load</span><span class="token punctuation">(</span><span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">request</span><span class="token operator">-></span><span class="token function">post</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token variable">$model</span><span class="token operator">-></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">redirect</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'view'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token variable">$model</span><span class="token operator">-></span><span class="token property">id</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">render</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'update'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">                <span class="token string single-quoted-string">'model'</span> <span class="token operator">=></span> <span class="token variable">$model</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line">    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">StaleObjectException</span> <span class="token variable">$e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// 解决冲突的代码</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// ------ Model 代码 -------</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>behaviors<span class="token punctuation">\</span>OptimisticLockBehavior</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">behaviors</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token class-name static-context">OptimisticLockBehavior</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">optimisticLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token string single-quoted-string">'version'</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Note: 因为 <code v-pre>yii\behaviors\OptimisticLockBehavior</code> 仅仅在保存记录的时候被确认，如果用户提交的有效版本号被直接解析：<code v-pre>yii\web\Request::getBodyParam()</code>，那么你的 Model 将扩展成这样：触发在步骤 3 中子类的行为，与此同时，调用步骤 2 中的父类的定义，这样你在把 Model 绑定到负责接收用户输入的控制器的同时，有一个专门用于内部逻辑处理的实例，或者，您可以通过配置其 <code v-pre>yii\behaviors\OptimisticLockBehavior::$value</code> 的属性来实现自己的逻辑。（注：这一堆都是在解释乐观锁行为的原理）</p>
</blockquote>
<h2 id="使用关联数据" tabindex="-1"><a class="header-anchor" href="#使用关联数据"><span>使用关联数据</span></a></h2>
<p>除了处理单个数据库表之外，AR 还可以将相关数据集中进来，使其可以通过原始数据轻松访问。例如，客户数据与订单数据相关，一个客户可能已经存放了一个或多个订单。这种关系通过适当的声明，你可以使用 <code v-pre>$customer-&gt;orders</code> 表达式访问客户的订单信息，这表达式将返回包含 <code v-pre>Order</code> AR 实例的客户订单信息的数组。</p>
<h3 id="声明关联关系" tabindex="-1"><a class="header-anchor" href="#声明关联关系"><span>声明关联关系</span></a></h3>
<p>你必须先在 AR 类中定义关联关系，才能使用 AR 的关联数据。简单地为每个需要定义关联关系声明一个 <em>关联方法</em> 即可，如下所示，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Order</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getCustomer</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasOne</span><span class="token punctuation">(</span><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'customer_id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述的代码中，我们为 <code v-pre>Customer</code> 类声明了一个 <code v-pre>orders</code> 关联，且为 <code v-pre>Order</code> 声明了一个 <code v-pre>customer</code> 关联。</p>
<p>每个关联方法必须这样命名：<code v-pre>getXyz</code>。然后我们通过 <code v-pre>xyz</code>（首字母小写）调用这个关联名。请注意关联名是大小写敏感的。</p>
<p>当声明一个关联关系的时候，必须指定好以下的信息：</p>
<ul>
<li>
<p>关联的对应关系：通过调用 [[yii\db\ActiveRecord::hasMany()]] 或者 [[yii\db\ActiveRecord::hasOne()]] 指定。在上面的例子中，您可以很容易看出这样的关联声明：一个客户可以有很多订单，而每个订单只有一个客户。</p>
</li>
<li>
<p>相关联 AR 类名：用来指定为 [[yii\db\ActiveRecord::hasMany()]] 或者 [[yii\db\ActiveRecord::hasOne()]] 方法的第一个参数。推荐的做法是调用 <code v-pre>Xyz::class</code> 来获取类名称的字符串，以便您可以使用 IDE 的自动补全，以及让编译阶段的错误检测生效。</p>
</li>
<li>
<p>两组数据的关联列：用以指定两组数据相关的列（hasOne()/hasMany() 的第二个参数）。数组的值填的是主数据的列（当前要声明关联的 AR 类为主数据），而数组的键要填的是相关数据的列。</p>
</li>
</ul>
<p>一个简单的口诀，先附表的主键，后主表的主键。正如上面的例子，<code v-pre>customer_id</code> 是 <code v-pre>Order</code> 的属性，而 <code v-pre>id</code>是 <code v-pre>Customer</code> 的属性。（译者注：hasMany() 的第二个参数，这个数组键值顺序不要弄反了）</p>
<h3 id="访问关联数据" tabindex="-1"><a class="header-anchor" href="#访问关联数据"><span>访问关联数据</span></a></h3>
<p>定义了关联关系后，你就可以通过关联名访问相应的关联数据了。就像访问一个由关联方法定义的对象一样，具体概念请查看 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-properties.md" target="_blank" rel="noopener noreferrer">属性</a>。因此，现在我们可以称它为 <em>关联属性</em> 了。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `id` = 123</span></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `customer_id` = 123</span></span>
<span class="line"><span class="token comment">// $orders 是由 Order 类组成的数组</span></span>
<span class="line"><span class="token variable">$orders</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Tip: 当你通过 getter 方法 <code v-pre>getXyz()</code> 声明了一个叫 <code v-pre>xyz</code> 的关联属性，你就可以像 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-properties.md" target="_blank" rel="noopener noreferrer">属性</a> 那样访问 <code v-pre>xyz</code>。注意这个命名是区分大小写的。</p>
</blockquote>
<p>如果使用 [[yii\db\ActiveRecord::hasMany()] 声明关联关系，则访问此关联属性将返回相关的 AR 实例的数组；如果使用 [[yii\db\ActiveRecord::hasOne()]] 声明关联关系，访问此关联属性 将返回相关的 AR 实例，如果没有找到相关数据的话，则返回 <code v-pre>null</code>。</p>
<p>当你第一次访问关联属性时，将执行 SQL 语句获取数据，如上面的例子所示。如果再次访问相同的属性，将返回先前的结果，而不会重新执行 SQL 语句。要强制重新执行 SQL 语句，你应该先 unset 这个关联属性，如：<code v-pre>unset（$ customer-&gt; orders）</code>。</p>
<blockquote>
<p>Tip: 虽然这个概念跟 这个 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-properties.md" target="_blank" rel="noopener noreferrer">属性</a> 特性很像，但是还是有一个很重要的区别。普通对象属性的属性值与其定义的 getter 方法的类型是相同的。而关联方法返回的是一个 [[yii\db\ActiveQuery]] 活动查询生成器的实例。只有当访问关联属性的的时候，才会返回 [[yii\db\ActiveRecord]] AR 实例，或者 AR 实例组成的数组。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">;</span> <span class="token comment">// 获得 `Order` 对象的数组</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 返回 ActiveQuery 类的实例</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div></blockquote>
<p>这对于创建自定义查询很有用，下一节将对此进行描述。</p>
<h3 id="动态关联查询" tabindex="-1"><a class="header-anchor" href="#动态关联查询"><span>动态关联查询</span></a></h3>
<p>由于关联方法返回 <code v-pre>yii\db\ActiveQuery</code> 的实例，因此你可以在执行 DB 查询之前，使用查询构建方法进一步构建此查询。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`</span></span>
<span class="line"><span class="token variable">$orders</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">where</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'>'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'subtotal'</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">orderBy</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>与访问关联属性不同，每次通过关联方法执行动态关联查询时，都会执行 SQL 语句，即使你之前执行过相同的动态关联查询。</p>
<p>有时你可能需要给你的关联声明传递参数，以便您能更方便地执行 动态关系查询。例如，您可以声明一个 <code v-pre>bigOrders</code> 关联如下，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getBigOrders</span><span class="token punctuation">(</span><span class="token variable">$threshold</span> <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">)</span> <span class="token comment">// 老司机的提醒：$threshold 参数一定一定要给个默认值</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">            <span class="token operator">-></span><span class="token function">where</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'subtotal > :threshold'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">':threshold'</span> <span class="token operator">=></span> <span class="token variable">$threshold</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">            <span class="token operator">-></span><span class="token function">orderBy</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>然后你就可以执行以下关联查询：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`</span></span>
<span class="line"><span class="token variable">$orders</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">getBigOrders</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 100 ORDER BY `id`</span></span>
<span class="line"><span class="token variable">$orders</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">bigOrders</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="连接表关联" tabindex="-1"><a class="header-anchor" href="#连接表关联"><span>连接表关联</span></a></h3>
<p>在数据库建模中，当两个关联表之间的对应关系是多对多时，通常会引入一个 <a href="https://en.wikipedia.org/wiki/Junction_table" target="_blank" rel="noopener noreferrer">连接表</a>。例如，<code v-pre>order</code> 表 和 <code v-pre>item</code> 表可以通过名为 <code v-pre>order_item</code> 的连接表相关联。一个 order 将关联多个 order items，而一个 order item 也会关联到多个 orders。</p>
<p>当声明这种表关联后，您可以调用 [[yii\db\ActiveQuery::via()]] 或 [[yii\db\ActiveQuery::viaTable()]] 指明连接表。[[yii\db\ActiveQuery::via()]] 和 [[yii\db\ActiveQuery::viaTable()]] 之间的区别是，前者是根据现有的关联名称来指定连接表，而后者直接使用连接表。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Order</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getItems</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Item</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'item_id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">            <span class="token operator">-></span><span class="token function">viaTable</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'order_item'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'order_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>或者,</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Order</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrderItems</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">OrderItem</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'order_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getItems</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Item</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'item_id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">            <span class="token operator">-></span><span class="token function">via</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orderItems'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>使用连接表声明的关联和正常声明的关联是等同的，例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `id` = 100</span></span>
<span class="line"><span class="token variable">$order</span> <span class="token operator">=</span> <span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// SELECT * FROM `order_item` WHERE `order_id` = 100</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `item` WHERE `item_id` IN (...)</span></span>
<span class="line"><span class="token comment">// 返回 Item 类组成的数组</span></span>
<span class="line"><span class="token variable">$items</span> <span class="token operator">=</span> <span class="token variable">$order</span><span class="token operator">-></span><span class="token property">items</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="多个表链接关联声明" tabindex="-1"><a class="header-anchor" href="#多个表链接关联声明"><span>多个表链接关联声明</span></a></h3>
<p>通过使用 [[yii\db\ActiveQuery::via()]] 方法，它还可以通过多个表来定义关联声明。再考虑考虑上面的例子，我们有 <code v-pre>Customer</code>，<code v-pre>Order</code> 和 <code v-pre>Item</code> 类。我们可以添加一个关联关系到 <code v-pre>Customer</code> 类，这个关联可以列出了 <code v-pre>Customer</code>（客户）的订单下放置的所有 <code v-pre>Item</code>（商品），这个关联命名为 <code v-pre>getPurchasedItems()</code>，关联声明如下代码示例所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getPurchasedItems</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// 客户的商品，将 Item 中的 'id' 列与 OrderItem 中的 'item_id' 相匹配</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Item</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'item_id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">                    <span class="token operator">-></span><span class="token function">via</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orderItems'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrderItems</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// 客户订单中的商品，将 `Order` 的 'id' 列和 OrderItem 的 'order_id' 列相匹配</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">OrderItem</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'order_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">                    <span class="token operator">-></span><span class="token function">via</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// 见上述列子</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="延迟加载和即时加载" tabindex="-1"><a class="header-anchor" href="#延迟加载和即时加载"><span>延迟加载和即时加载</span></a></h3>
<p>在 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md#accessing-relational-data" target="_blank" rel="noopener noreferrer">访问关联数据</a> 中，我们解释说可以像问正常的对象属性那样 访问 AR 实例的关联属性。SQL 语句仅在你第一次访问关联属性时执行。我们称这种关联数据访问方法为 <em>延迟加载</em>。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `id` = 123</span></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `customer_id` = 123</span></span>
<span class="line"><span class="token variable">$orders</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 没有 SQL 语句被执行</span></span>
<span class="line"><span class="token variable">$orders2</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>延迟加载使用非常方便。但是，当你需要访问相同的具有多个 AR 实例的关联属性时，可能会遇到性能问题。请思考一下以下代码示例。有多少 SQL 语句会被执行？</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// SELECT * FROM `customer` LIMIT 100</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">limit</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token variable">$customers</span> <span class="token keyword">as</span> <span class="token variable">$customer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// SELECT * FROM `order` WHERE `customer_id` = ...</span></span>
<span class="line">    <span class="token variable">$orders</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>你瞅瞅，上面的代码会产生 101 次 SQL 查询！这是因为每次你访问 for 循环中不同的 <code v-pre>Customer</code> 对象的 <code v-pre>orders</code> 关联属性时，SQL 语句都会被执行一次。</p>
<p>为了解决上述的性能问题，你可以使用所谓的 <em>即时加载</em>，如下所示，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// SELECT * FROM `customer` LIMIT 100;</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `orders` WHERE `customer_id` IN (...)</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">limit</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token variable">$customers</span> <span class="token keyword">as</span> <span class="token variable">$customer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// 没有任何的 SQL 执行</span></span>
<span class="line">    <span class="token variable">$orders</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>通过调用 [[yii\db\ActiveQuery::with()]] 方法，你使 AR 在一条 SQL 语句里就返回了这 100 位客户的订单。结果就是，你把要执行的 SQL 语句从 101 减少到 2 条！</p>
<p>你可以即时加载一个或多个关联。你甚至可以即时加载 <em>嵌套关联</em> 。嵌套关联是一种在相关的 AR 类中声明的关联。例如，<code v-pre>Customer</code> 通过 <code v-pre>orders</code> 关联属性 与 <code v-pre>Order</code> 相关联，<code v-pre>Order</code> 与 <code v-pre>Item</code> 通过 <code v-pre>items</code> 关联属性相关联。当查询 <code v-pre>Customer</code> 时，您可以即时加载 通过嵌套关联符 <code v-pre>orders.items</code> 关联的 <code v-pre>items</code>。</p>
<p>以下代码展示了 [[yii\db\ActiveQuery::with()]] 的各种用法。我们假设 <code v-pre>Customer</code> 类有两个关联 <code v-pre>orders</code> 和 <code v-pre>country</code>，而 <code v-pre>Order</code> 类有一个关联 <code v-pre>items</code>。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">//  即时加载 "orders" and "country"</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'country'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// 等同于使用数组语法 如下</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'country'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// 没有任何的 SQL 执行</span></span>
<span class="line"><span class="token variable">$orders</span><span class="token operator">=</span> <span class="token variable">$customers</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// 没有任何的 SQL 执行</span></span>
<span class="line"><span class="token variable">$country</span> <span class="token operator">=</span> <span class="token variable">$customers</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">-></span><span class="token property">country</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 即时加载“订单”和嵌套关系“orders.items”</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders.items'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// 访问第一个客户的第一个订单中的商品</span></span>
<span class="line"><span class="token comment">// 没有 SQL 查询执行</span></span>
<span class="line"><span class="token variable">$items</span> <span class="token operator">=</span> <span class="token variable">$customers</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">-></span><span class="token property">items</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>你也可以即时加载更深的嵌套关联，比如 <code v-pre>a.b.c.d</code>。所有的父关联都会被即时加载。那就是, 当你调用 [[yii\db\ActiveQuery::with()]] 来 with <code v-pre>a.b.c.d</code>，你将即时加载 <code v-pre>a</code>，<code v-pre>a.b</code>，<code v-pre>a.b.c</code> and <code v-pre>a.b.c.d</code>。</p>
<blockquote>
<p>Tip: 一般来说，当即时加载 <code v-pre>N</code> 个关联，另有 <code v-pre>M</code> 个关联 通过 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md#junction-table" target="_blank" rel="noopener noreferrer">连接表</a> 声明，则会有 <code v-pre>N+M+1</code> 条 SQL 语句被执行。请注意这样的的嵌套关联 <code v-pre>a.b.c.d</code> 算四个关联。</p>
</blockquote>
<p>当即时加载一个关联，你可以通过匿名函数自定义相应的关联查询。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 查找所有客户，并带上他们国家和活跃订单</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `customer`</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `country` WHERE `id` IN (...)</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'country'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'orders'</span> <span class="token operator">=></span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$query</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$query</span><span class="token operator">-></span><span class="token function">andWhere</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token constant">STATUS_ACTIVE</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>自定义关联查询时，应该将关联名称指定为数组的键，并使用匿名函数作为相应的数组的值。匿名函数将接受一个 <code v-pre>$query</code> 参数，它用于表示这个自定义的关联执行关联查询的 <code v-pre>yii\db\ActiveQuery</code> 对象。在上面的代码示例中，我们通过附加一个关于订单状态的附加条件来修改关联查询。</p>
<blockquote>
<p>Tip: 如果你在即时加载的关联中调用 [[yii\db\Query::select()]] 方法，你要确保 在关联声明中引用的列必须被 select。否则，相应的模型（Models）可能 无法加载。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$orders</span> <span class="token operator">=</span> <span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'amount'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'customer'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// $orders[0]->customer 会一直是 `null`。你应该这样写，以解决这个问题：</span></span>
<span class="line"><span class="token variable">$orders</span> <span class="token operator">=</span> <span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'id'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'amount'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'customer_id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'customer'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></blockquote>
<h3 id="关联关系的-join-查询" tabindex="-1"><a class="header-anchor" href="#关联关系的-join-查询"><span>关联关系的 JOIN 查询</span></a></h3>
<blockquote>
<p>Tip: 这小节的内容仅仅适用于关系数据库，比如 MySQL，PostgreSQL 等等。</p>
</blockquote>
<p>到目前为止，我们所介绍的关联查询，仅仅是使用主表列去查询主表数据。实际应用中，我们经常需要在关联表中使用这些列。例如，我们可能要取出至少有一个活跃订单的客户。为了解决这个问题，我们可以构建一个 join 查询，如下所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// SELECT `customer`.* FROM `customer`</span></span>
<span class="line"><span class="token comment">// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id`</span></span>
<span class="line"><span class="token comment">// WHERE `order`.`status` = 1</span></span>
<span class="line"><span class="token comment">//</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `customer_id` IN (...)</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">select</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'customer.*'</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">leftJoin</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'order'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'`order`.`customer_id` = `customer`.`id`'</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">where</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'order.status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token constant">STATUS_ACTIVE</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Tip: 在构建涉及 JOIN SQL 语句的连接查询时，清除列名的歧义很重要。 通常的做法是将表名称作为前缀加到对应的列名称前。</p>
</blockquote>
<p>但是，更好的方法是通过调用 [[yii\db\ActiveQuery::joinWith()]] 来利用已存在的关联声明：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">joinWith</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">where</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'order.status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token constant">STATUS_ACTIVE</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>两种方法都执行相同的 SQL 语句集。然而，后一种方法更干净、简洁。</p>
<p>默认的，[[yii\db\ActiveQuery::joinWith()]] 会使用 <code v-pre>LEFT JOIN</code> 去连接主表和关联表。 你可以通过 <code v-pre>$joinType</code> 参数指定不同的连接类型（比如 <code v-pre>RIGHT JOIN</code>）。如果你想要的连接类型是 <code v-pre>INNER JOIN</code>，你可以直接用 [[yii\db\ActiveQuery::innerJoinWith()]] 方法代替。</p>
<p>调用 [[yii\db\ActiveQuery::joinWith()]] 方法会默认 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md#lazy-eager-loading" target="_blank" rel="noopener noreferrer">即时加载</a> 相应的关联数据。如果你不需要那些关联数据，你可以指定它的第二个参数 <code v-pre>$eagerLoading</code> 为 <code v-pre>false</code>。</p>
<blockquote>
<p>Note: 即使在启用即时加载的情况下使用 [[yii\db\ActiveQuery::joinWith()]] 或 [[yii\db\ActiveQuery::innerJoinWith()]]，相应的关联数据也 <strong>不会</strong> 从这个 <code v-pre>JOIN</code> 查询的结果中填充。因此，每个连接关系还有一个额外的查询，正如 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md#lazy-eager-loading" target="_blank" rel="noopener noreferrer">即时加载</a> 部分所述。</p>
</blockquote>
<p>和 [[yii\db\ActiveQuery::with()]] 一样，你可以 join 多个关联表；你可以动态的自定义你的关联查询；你可以使用嵌套关联进行 join。你也可以将 [[yii\db\ActiveQuery::with()]] 和 [[yii\db\ActiveQuery::joinWith()]] 组合起来使用。例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">joinWith</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'orders'</span> <span class="token operator">=></span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$query</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$query</span><span class="token operator">-></span><span class="token function">andWhere</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'>'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'subtotal'</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'country'</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>有时，当连接两个表时，你可能需要在 JOIN 查询的 <code v-pre>ON</code> 部分中指定一些额外的条件。 这可以通过调用 [[yii\db\ActiveQuery::onCondition()]] 方法来完成，如下所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// SELECT `customer`.* FROM `customer`</span></span>
<span class="line"><span class="token comment">// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 1</span></span>
<span class="line"><span class="token comment">//</span></span>
<span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `customer_id` IN (...)</span></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">joinWith</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'orders'</span> <span class="token operator">=></span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$query</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$query</span><span class="token operator">-></span><span class="token function">onCondition</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'order.status'</span> <span class="token operator">=></span> <span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token constant">STATUS_ACTIVE</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>以上查询取出 <em>所有</em> 客户，并为每个客户取回所有活跃订单。 请注意，这与我们之前的例子不同，后者仅取出至少有一个活跃订单的客户。</p>
<blockquote>
<p>Tip: 当通过 [[yii\db\ActiveQuery::onCondition()]] 修改 [[yii\db\ActiveQuery]] 时，如果查询涉及到 JOIN 查询，那么条件将被放在 <code v-pre>ON</code> 部分。如果查询不涉及 JOIN ，条件将自动附加到查询的 <code v-pre>WHERE</code> 部分。因此，它可以只包含“包含了关联表的列”的条件。（译者注：意思是 onCondition() 中可以只写关联表的列，主表的列写不写都行）</p>
</blockquote>
<h4 id="关联表别名" tabindex="-1"><a class="header-anchor" href="#关联表别名"><span>关联表别名</span></a></h4>
<p>如前所述，当在查询中使用 JOIN 时，我们需要消除列名的歧义。因此通常为一张表定义一个别名。可以通过以下列方式自定义关联查询来设置关联查询的别名：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$query</span><span class="token operator">-></span><span class="token function">joinWith</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'orders'</span> <span class="token operator">=></span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$q</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$q</span><span class="token operator">-></span><span class="token function">from</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'o'</span> <span class="token operator">=></span> <span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token function">tableName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>然而，这看起来很复杂和耦合，不管是对表名使用硬编码或是调用 <code v-pre>Order::tableName()</code>。从 2.0.7 版本起，Yii 为此提供了一个快捷方法。您现在可以定义和使用关联表的别名，如下所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 连接 `orders` 关联表并根据 `orders.id` 排序</span></span>
<span class="line"><span class="token variable">$query</span><span class="token operator">-></span><span class="token function">joinWith</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'orders o'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">orderBy</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'o.id'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><p>上述语法适用于简单的关联。如果在 join 嵌套关联时，需要用到中间表的别名，例如 <code v-pre>$query-&gt;joinWith(['orders.product'])</code>，你需要嵌套 joinWith 调用，如下例所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$query</span><span class="token operator">-></span><span class="token function">joinWith</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'orders o'</span> <span class="token operator">=></span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token variable">$q</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$q</span><span class="token operator">-></span><span class="token function">joinWith</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'product p'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">where</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'o.amount > 100'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="反向关联" tabindex="-1"><a class="header-anchor" href="#反向关联"><span>反向关联</span></a></h3>
<p>两个 AR 类之间的关联声明往往是相互关联的。例如，<code v-pre>Customer</code> 是通过 <code v-pre>orders</code> 关联到 <code v-pre>Order</code> ，而<code v-pre>Order</code> 通过 <code v-pre>customer</code> 又关联回到了 <code v-pre>Customer</code>。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Order</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getCustomer</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasOne</span><span class="token punctuation">(</span><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'customer_id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>现在考虑下面的一段代码：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `id` = 123</span></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `customer_id` = 123</span></span>
<span class="line"><span class="token variable">$order</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `id` = 123</span></span>
<span class="line"><span class="token variable">$customer2</span> <span class="token operator">=</span> <span class="token variable">$order</span><span class="token operator">-></span><span class="token property">customer</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 显示 "not the same"</span></span>
<span class="line"><span class="token keyword">echo</span> <span class="token variable">$customer2</span> <span class="token operator">===</span> <span class="token variable">$customer</span> <span class="token operator">?</span> <span class="token string single-quoted-string">'same'</span> <span class="token punctuation">:</span> <span class="token string single-quoted-string">'not the same'</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>我们原本认为 <code v-pre>$customer</code> 和 <code v-pre>$customer2</code> 是一样的，但不是！其实他们确实包含相同的客户数据，但它们是不同的对象。访问 <code v-pre>$order-&gt;customer</code> 时，需要执行额外的 SQL 语句，以填充出一个新对象 <code v-pre>$customer2</code>。</p>
<p>为了避免上述例子中最后一个 SQL 语句被冗余执行，我们应该告诉 Yii <code v-pre>customer</code> 是 <code v-pre>orders</code> 的 <em>反向关联</em>，可以通过调用 [[yii\db\ActiveQuery::inverseOf()]] 方法声明，如下所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">inverseOf</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'customer'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>这样修改关联声明后：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// SELECT * FROM `customer` WHERE `id` = 123</span></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// SELECT * FROM `order` WHERE `customer_id` = 123</span></span>
<span class="line"><span class="token variable">$order</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// No SQL will be executed</span></span>
<span class="line"><span class="token variable">$customer2</span> <span class="token operator">=</span> <span class="token variable">$order</span><span class="token operator">-></span><span class="token property">customer</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 输出 "same"</span></span>
<span class="line"><span class="token keyword">echo</span> <span class="token variable">$customer2</span> <span class="token operator">===</span> <span class="token variable">$customer</span> <span class="token operator">?</span> <span class="token string single-quoted-string">'same'</span> <span class="token punctuation">:</span> <span class="token string single-quoted-string">'not the same'</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Note: 反向关联不能用在有 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md#junction-table" target="_blank" rel="noopener noreferrer">连接表</a> 关联声明中。也就是说，如果一个关联关系通过 [[yii\db\ActiveQuery::via()]] 或 [[yii\db\ActiveQuery::viaTable()]] 声明，你就不能再调用 [[yii\db\ActiveQuery::inverseOf()]] 了。</p>
</blockquote>
<h2 id="保存或销毁关联" tabindex="-1"><a class="header-anchor" href="#保存或销毁关联"><span>保存或销毁关联</span></a></h2>
<p>在使用关联数据时，您经常需要建立不同数据之间的关联或销毁现有关联。这需要为定义的关联的列设置正确的值。通过使用 AR，你就可以编写如下代码：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$order</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Order</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$order</span><span class="token operator">-></span><span class="token property">subtotal</span> <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// ...</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 为 Order 设置属性以定义与 "customer" 的关联关系</span></span>
<span class="line"><span class="token variable">$order</span><span class="token operator">-></span><span class="token property">customer_id</span> <span class="token operator">=</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">id</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$order</span><span class="token operator">-></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>AR 提供了 [[yii\db\ActiveRecord::link()]] 方法，可以更好地完成此任务：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$order</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Order</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$order</span><span class="token operator">-></span><span class="token property">subtotal</span> <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// ...</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$order</span><span class="token operator">-></span><span class="token function">link</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'customer'</span><span class="token punctuation">,</span> <span class="token variable">$customer</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>[[yii\db\ActiveRecord::link()]] 方法需要指定关联名和要建立关联的目标 AR 实例。该方法将修改属性的值以连接两个 AR 实例，并将其保存到数据库。在上面的例子中，它将设置 <code v-pre>Order</code> 实例的 <code v-pre>customer_id</code> 属性为 <code v-pre>Customer</code> 实例的 <code v-pre>id</code> 属性的值，然后保存到数据库。</p>
<blockquote>
<p>Note: 你不能关联两个新的 AR 实例。</p>
</blockquote>
<p>使用 [[yii\db\ActiveRecord::link()]] 的好处在通过 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md#junction-table" target="_blank" rel="noopener noreferrer">连接表</a> 定义关系时更加明显。例如，你可以使用以下代码关联 <code v-pre>Order</code> 实例和 <code v-pre>Item</code> 实例：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$order</span><span class="token operator">-></span><span class="token function">link</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'items'</span><span class="token punctuation">,</span> <span class="token variable">$item</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>上述代码会自动在 <code v-pre>order_item</code> 关联表中插入一行，以关联 order 和 item 这两个数据记录。</p>
<blockquote>
<p>Info: [[yii\db\ActiveRecord::link()]] 方法在保存相应的 AR 实例时，将不会执行任何数据验证。在调用此方法之前，您应当验证所有的输入数据。</p>
</blockquote>
<p>[[yii\db\ActiveRecord::link()]] 方法的反向操作是 [[yii\db\ActiveRecord::unlink()]] 方法，这将可以断掉两个 AR 实例间的已经存在了的关联关系。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">where</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token number">123</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">one</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token function">unlink</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">,</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">orders</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><p>默认情况下，[[yii\db\ActiveRecord::unlink()]] 方法将设置指定的外键值，以把现有的关联指定为 <code v-pre>null</code>。此外，你可以选择通过将 <code v-pre>$delete</code> 参数设置为<code v-pre>true</code> 传递给方法，删除包含此外键值的表记录行。</p>
<p>当关联关系中有连接表时，调用 [[yii\db\ActiveRecord::unlink()]] 时，如果 <code v-pre>$delete</code> 参数是 <code v-pre>true</code> 的话，将导致连接表中的外键或相应的行被删除。</p>
<h2 id="跨数据库关联" tabindex="-1"><a class="header-anchor" href="#跨数据库关联"><span>跨数据库关联</span></a></h2>
<p>AR 允许您在不同数据库驱动的 AR 类之间声明关联关系。这些数据库可以是不同的类型（例如 MySQL 和 PostgreSQL ，或是 MS SQL 和 MongoDB），它们也可以运行在不同的服务器上。你可以使用相同的语法来执行关联查询。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// Customer 对应的表是关系数据库中（比如 MySQL）的 "customer" 表</span></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">tableName</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token string single-quoted-string">'customer'</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getComments</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// 一个 customer 有很多条评论（comments）</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Comment</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// Comment 对应的是 MongoDB 数据库中的  "comment" 集合（译者注：MongoDB 中的集合相当于 MySQL 中的表）</span></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Comment</span> <span class="token keyword">extends</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>mongodb<span class="token punctuation">\</span>ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">collectionName</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token string single-quoted-string">'comment'</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getCustomer</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// 一条评论对应一位 customer</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasOne</span><span class="token punctuation">(</span><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'customer_id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'comments'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>本节中描述的大多数关联查询功能，你都可以抄一抄。</p>
<blockquote>
<p>Note: [[yii\db\ActiveQuery::joinWith()]] 这个功能限制于某些数据库是否支持跨数据库 JOIN 查询。因此，你再上述的代码里就不能用此方法了，因为 MongoDB 不支持 JOIN 查询。</p>
</blockquote>
<h2 id="自定义查询类" tabindex="-1"><a class="header-anchor" href="#自定义查询类"><span>自定义查询类</span></a></h2>
<p>默认情况下，<code v-pre>yii\db\ActiveQuery</code> 支持所有 AR 查询。要在 AR 类中使用自定义的查询类，您应该重写 [[yii\db\ActiveRecord::find()]] 方法并返回一个你自定义查询类的实例。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// file Comment.php</span></span>
<span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>models</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Comment</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">CommentQuery</span><span class="token punctuation">(</span><span class="token function">get_called_class</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>现在，对于 <code v-pre>Comment</code> 类，不管你执行查询（如 <code v-pre>find()</code>、<code v-pre>findOne()</code>），还是定义一个关联（如 <code v-pre>hasOne()</code>），你都将调用到 <code v-pre>CommentQuery</code> 实例，而不再是 <code v-pre>ActiveQuery</code> 实例。</p>
<p>现在你可以定义 <code v-pre>CommentQuery</code> 类了，发挥你的奇技淫巧，以改善查询构建体验。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// file CommentQuery.php</span></span>
<span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>models</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveQuery</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">CommentQuery</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveQuery</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// 默认加上一些条件（可以跳过）</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">andOnCondition</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'deleted'</span> <span class="token operator">=></span> <span class="token constant boolean">false</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token keyword static-context">parent</span><span class="token operator">::</span><span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// ... 在这里加上自定义的查询方法 ...</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">active</span><span class="token punctuation">(</span><span class="token variable">$state</span> <span class="token operator">=</span> <span class="token constant boolean">true</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">andOnCondition</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'active'</span> <span class="token operator">=></span> <span class="token variable">$state</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Note: 作为 [[yii\db\ActiveQuery::onCondition()]] 方法的替代方案，你应当调用 [[yii\db\ActiveQuery::andOnCondition()]] 或 [[yii\db\ActiveQuery::orOnCondition()]] 方法来附加新增的条件，不然在一个新定义的查询方法，已存在的条件可能会被覆盖。</p>
</blockquote>
<p>然后你就可以先下面这样构建你的查询了：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$comments</span> <span class="token operator">=</span> <span class="token class-name static-context">Comment</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">active</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$inactiveComments</span> <span class="token operator">=</span> <span class="token class-name static-context">Comment</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">active</span><span class="token punctuation">(</span><span class="token constant boolean">false</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Tip: 在大型项目中，建议您使用自定义查询类来容纳大多数与查询相关的代码，以使 AR 类保持简洁。</p>
</blockquote>
<p>您还可以在 <code v-pre>Comment</code> 关联关系的定义中或在执行关联查询时，使用刚刚新建查询构建方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getActiveComments</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Comment</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">active</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">joinWith</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'activeComments'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 或者这样</span></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getComments</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Comment</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">joinWith</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'comments'</span> <span class="token operator">=></span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token variable">$q</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$q</span><span class="token operator">-></span><span class="token function">active</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Tip: 在 Yii 1.1 中，有个概念叫做 <em>命名范围</em>。命名范围在 Yii 2.0 中不再支持，你依然可以使用自定义查询类、查询方法来达到一样的效果。</p>
</blockquote>
<h2 id="select-额外字段" tabindex="-1"><a class="header-anchor" href="#select-额外字段"><span>SELECT 额外字段</span></a></h2>
<p>当 AR 实例从查询结果中填充时，从数据结果集中，其属性的值将被相应的列填充。</p>
<p>你可以从查询中获取其他列或值，并将其存储在 AR 活动记录中。例如，假设我们有一个名为 <code v-pre>room</code> 的表，其中包含有关酒店可用房间的信息。每个房间使用字段 <code v-pre>length</code>，<code v-pre>width</code>，<code v-pre>height</code> 存储有关其空间大小的信息。想象一下，我们需要检索出所有可用房间的列表，并按照体积大小倒序排列。你不可能使用 PHP 来计算体积，但是，由于我们需要按照它的值对这些记录进行排序，你依然需要 <code v-pre>volume</code>（体积）来显示在这个列表中。为了达到这个目标，你需要在你的 <code v-pre>Room</code> 活动记录类中声明一个额外的字段，它将存储 <code v-pre>volume</code> 的值：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Room</span> <span class="token keyword">extends</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$volume</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>然后，你需要撰写一个查询，它可以计算房间的大小并执行排序：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$rooms</span> <span class="token operator">=</span> <span class="token class-name static-context">Room</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'{{room}}.*'</span><span class="token punctuation">,</span> <span class="token comment">// select all columns</span></span>
<span class="line">        <span class="token string single-quoted-string">'([[length]] * [[width]] * [[height]]) AS volume'</span><span class="token punctuation">,</span> <span class="token comment">// 计算体积</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">orderBy</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'volume DESC'</span><span class="token punctuation">)</span> <span class="token comment">// 使用排序</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token variable">$rooms</span> <span class="token keyword">as</span> <span class="token variable">$room</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">echo</span> <span class="token variable">$room</span><span class="token operator">-></span><span class="token property">volume</span><span class="token punctuation">;</span> <span class="token comment">// 包含了由 SQL 计算出的值</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>额外字段的特性对于聚合查询非常有用。假设您需要显示一系列客户的订单数量。首先，您需要使用 <code v-pre>orders</code> 关系声明一个 <code v-pre>Customer</code> 类，并指定额外字段来存储 count 结果：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$ordersCount</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>然后你可以编写一个查询来 JOIN 订单表，并计算订单的总数：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$customers</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'{{customer}}.*'</span><span class="token punctuation">,</span> <span class="token comment">// select customer 表所有的字段</span></span>
<span class="line">        <span class="token string single-quoted-string">'COUNT({{order}}.id) AS ordersCount'</span> <span class="token comment">// 计算订单总数</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">joinWith</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'orders'</span><span class="token punctuation">)</span> <span class="token comment">// 连接表</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">groupBy</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'{{customer}}.id'</span><span class="token punctuation">)</span> <span class="token comment">// 分组查询，以确保聚合函数生效</span></span>
<span class="line">    <span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>使用此方法的一个缺点是，如果数据不是从 SQL 查询上加载的，它必须再单独计算一遍。因此，如果你通过常规查询获取个别的数据记录时，它没有额外的 select 语句，那么它将无法返回额外字段的实际值。新保存的记录一样会发生这种情况。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$room</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Room</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$room</span><span class="token operator">-></span><span class="token property">length</span> <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$room</span><span class="token operator">-></span><span class="token property">width</span> <span class="token operator">=</span> <span class="token number">50</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$room</span><span class="token operator">-></span><span class="token property">height</span> <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$room</span><span class="token operator">-></span><span class="token property">volume</span><span class="token punctuation">;</span> <span class="token comment">// 为 `null`, 因为它没有被声明（赋值）</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>通过 [[yii\db\BaseActiveRecord:😗*get()]] 和 [[yii\db\BaseActiveRecord:😗*set()]] 魔术方法我们可以将属性赋予行为特性：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Room</span> <span class="token keyword">extends</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">private</span> <span class="token variable">$_volume</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">setVolume</span><span class="token punctuation">(</span><span class="token variable">$volume</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">_volume</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword type-casting">float</span><span class="token punctuation">)</span> <span class="token variable">$volume</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getVolume</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">empty</span><span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token property">length</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token keyword">empty</span><span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token property">width</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token keyword">empty</span><span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token property">height</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token keyword">return</span> <span class="token constant">null</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token property">_volume</span> <span class="token operator">===</span> <span class="token constant">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">setVolume</span><span class="token punctuation">(</span></span>
<span class="line">                <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">length</span> <span class="token operator">*</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">width</span> <span class="token operator">*</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">height</span></span>
<span class="line">            <span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">_volume</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>当 select 查询不提供 volume 体积时，这模型将能够自动计算体积的值出来，当访问模型的属性的时候。</p>
<p>当定义关联关系的时候，你也可以计算聚合字段：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">private</span> <span class="token variable">$_ordersCount</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">setOrdersCount</span><span class="token punctuation">(</span><span class="token variable">$count</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">_ordersCount</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword type-casting">int</span><span class="token punctuation">)</span> <span class="token variable">$count</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrdersCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token property">isNewRecord</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token keyword">return</span> <span class="token constant">null</span><span class="token punctuation">;</span> <span class="token comment">// 这样可以避免调用空主键进行查询</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token property">_ordersCount</span> <span class="token operator">===</span> <span class="token constant">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">setOrdersCount</span><span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">count</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 根据关联关系按需计算聚合字段</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">_ordersCount</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>使用此代码，如果 ‘select’ 语句中存在 ‘ordersCount’ - 它会从查询结果集获取数据填充 <code v-pre>Customer::ordersCount</code> 属性，否则它会在被访问的时候，使用 <code v-pre>Customer::orders</code> 关联按需计算。</p>
<p>这种方法也适用于创建一些关联数据的快捷访问方式，特别是对于聚合。例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Customer</span> <span class="token keyword">extends</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * 为聚合数据定义一个只读的虚拟属性</span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrdersCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token property">isNewRecord</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token keyword">return</span> <span class="token constant">null</span><span class="token punctuation">;</span> <span class="token comment">//  这样可以避免调用空主键进行查询</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">        <span class="token keyword">return</span> <span class="token keyword">empty</span><span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-></span><span class="token property">ordersAggregation</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token number">0</span> <span class="token punctuation">:</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">ordersAggregation</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'counted'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * 声明一个常规的 'orders' 关联</span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">hasMany</span><span class="token punctuation">(</span><span class="token class-name static-context">Order</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'id'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token doc-comment comment">/**</span>
<span class="line">     * 基于 'orders' 关联，声明一个用于查询聚合的新关联</span>
<span class="line">     */</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getOrdersAggregation</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">getOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">            <span class="token operator">-></span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'customer_id'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'counted'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'count(*)'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">            <span class="token operator">-></span><span class="token function">groupBy</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'customer_id'</span><span class="token punctuation">)</span></span>
<span class="line">            <span class="token operator">-></span><span class="token function">asArray</span><span class="token punctuation">(</span><span class="token constant boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">with</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'ordersAggregation'</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token variable">$customer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">echo</span> <span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">ordersCount</span><span class="token punctuation">;</span> <span class="token comment">// 输出关联的聚合数据，而不需要额外的查询，因为我们用了即时加载</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$customer</span> <span class="token operator">=</span> <span class="token class-name static-context">Customer</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token variable">$pk</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$customer</span><span class="token operator">-></span><span class="token property">ordersCount</span><span class="token punctuation">;</span> <span class="token comment">// 从延迟加载的关联中，输出聚合数据</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>💖喜欢本文档的，欢迎点赞、收藏、留言或转发，谢谢支持！<br>
作者邮箱：zhuzixian520@126.com，github地址：<a href="https://github.com/zhuzixian520" target="_blank" rel="noopener noreferrer">github.com/zhuzixian520</a></p>
</blockquote>
</div></template>


